using System.Collections.Generic;
using MemoryPack;

namespace Game {
	[MemoryPackable]
	public partial class BaseInventory {
		public List<BaseInventoryItem> Items;

		List<BaseInventoryItem> _tmpList = new List<BaseInventoryItem>(8);

		public int SlotCount { get; protected set; }

		public int ItemCount {
			get {
				var result = 0;
				foreach ( var item in Items ) {
					result += item?.Count ?? 0;
				}
				return result;
			}
		}

		public int FreeSlotsCount {
			get {
				return SlotCount - OccupiedSlotsCount;
			}
		}

		public int OccupiedSlotsCount {
			get {
				var result = 0;
				foreach ( var item in Items ) {
					result += item != null ? 1 : 0;
				}
				return result;
			}
		}

		public BaseInventory(int slotCount) {
			Items = new List<BaseInventoryItem>(slotCount);
			SlotCount = slotCount;
		}

		public void Clear() {
			Items.Clear();
			_tmpList.Clear();
		}

		public BaseInventoryItem GetItemInSlot(int slotIndex) {
			if ( slotIndex < 0 || slotIndex >= Items.Count ) {
				return null;
			}
			var item = Items[slotIndex];
			return item;
		}

		public BaseInventoryItem DropItemsFromSlot(int slotIndex, int count = int.MaxValue) {
			if ( slotIndex < 0 || slotIndex >= Items.Count ) {
				return null;
			}
			var item = GetItemInSlot(slotIndex);
			if ( item == null ) {
				return null;
			}
			if ( item.Count <= count ) {
				Items[slotIndex] = null;
				return item;
			}
			var droppedItem = item.SplitPartToStack(count);
			return droppedItem;
		}

		public bool AddItem(BaseInventoryItem item, bool tryMergeStacks) {
			if ( item == null ) {
				return false;
			}
			if ( Items.Count >= SlotCount && !tryMergeStacks ) {
				return false;
			}
			var stackSize = CommonStaticData.GetInventoryStackSize(item);
			if ( stackSize <= 1 && Items.Count >= SlotCount ) {
				return false;
			}
			var added = false;
			if ( tryMergeStacks ) {
				added = TryAddToExistingStacks(item);
				if ( item.Count == 0 && added) {
					return true;
				}
			}
			added |= TryAddToFreeStack(item);
			return added;
		}

		bool TryAddToFreeStack(BaseInventoryItem item) {
			for ( int i = 0; i < Items.Count; i++ ) {
				if ( Items[i] == null ) {
					Items[i] = item;
					return true;
				}
			}
			if ( Items.Count >= SlotCount ) {
				return false;
			}
			var firstFreeStack = Items.Count > 0 ? Items.Count - 1 : 0;
			if ( firstFreeStack >= 0 && firstFreeStack < SlotCount ) {
				Items.Add(item);
				return true;
			}
			return false;
		}

		public bool ConsumeItem(BaseInventoryItem item, int count) {
			if ( item == null ) {
				return false;
			}
			for ( int i = 0; i < Items.Count; i++ ) {
				if ( Items[i] == null ) {
					continue;
				}
				if ( Items[i].IsEqualKind(item) && Items[i].Count >= count ) {
					Items[i].Count -= count;
					if ( Items[i].Count == 0 ) {
						Items[i] = null;
					}
					return true;
				}
			}
			return false;
		}

		public bool AddItems(List<BaseInventoryItem> items) {
			if ( items == null || items.Count == 0 ) {
				return false;
			}
			var anyItemAdded = false;
			_tmpList.Clear();
			foreach ( var item in items ) {
				if ( AddItem(item, true) ) {
					anyItemAdded = true;
					if ( item.Count <= 0 ) {
						_tmpList.Add(item); //этот стак добавили полностью, убираем его из переданного списка
					}
				}
			}
			if ( !anyItemAdded ) {
				return false;
			}
			if ( _tmpList.Count > 0 ) {
				foreach ( var item in _tmpList ) {
					items.Remove(item);
				}
			}
			return true;
		}

		public bool TryAddToExistingStacks(BaseInventoryItem item) {
			var stackSize = CommonStaticData.GetInventoryStackSize(item);
			if ( stackSize <= 1 ) {
				return false;
			}
			var inCount = item.Count;
			_tmpList.Clear();
			FindItemsWithType(item.ItemType, _tmpList);
			foreach ( var i in _tmpList ) {
				if ( i.CanStack(item) ) {
					i.AddToStack(item);
					if ( item.Count <= 0 ) {
						break;
					}
				}
			}
			if ( item.Count < inCount ) {
				return true;
			}
			return false;
		}

		public bool FindItemsWithType(InventoryItemType type, List<BaseInventoryItem> output) {
			var hasItems = false;
			foreach ( var item in Items ) {
				if ( item != null && item.ItemType == type ) {
					output.Add(item);
					hasItems = true;
				}
			}
			return hasItems;
		}

		public int GetWholeInventoryPrice() {
			var sum = 0;
			foreach ( var item in Items ) {
				if ( item != null ) {
					sum += item.GetTotalPrice();
				}				
			}
			return sum;
		}
	}
}
